import express, {Express, NextFunction, Request, RequestHandler, Response} from 'express';
import cors from 'cors';
import compression from 'compression';
import {createProxyMiddleware} from 'http-proxy-middleware';
import basicAuth from 'express-basic-auth';
import {ServerConfig} from '../types';
import {Logger} from '../utils/Logger';
import path from 'path';
import fs from 'fs';
import {FileUtils} from "../utils/FileUtils";

/**
 * 中间件工厂类，用于创建和配置Express中间件
 */
export class MiddlewareFactory {
  private logger: Logger;
  private config: ServerConfig;

  constructor(config: ServerConfig) {
    this.config = config;
    this.logger = Logger.getInstance();
  }

  /**
   * 创建并配置所有中间件
   */
  public configureMiddleware(app: Express): void {
    // 日志中间件
    app.use(this.createLoggerMiddleware());

    // 基本认证
    if (this.config.security?.basicAuth?.enabled) {
      this.configureBasicAuth(app);
    }

    // CORS支持
    if (this.config.cors) {
      app.use(cors());
      this.logger.info('已启用CORS支持');
    }

    // Gzip压缩
    if (this.config.compress) {
      app.use(compression());
      this.logger.info('已启用Gzip压缩');
    }

    // 代理中间件
    this.configureProxyMiddleware(app);

    // 配置静态文件服务
    this.configureStaticServer(app);
  }

  /**
   * 创建日志中间件
   */
  private createLoggerMiddleware(): RequestHandler {
    return (req, res, next) => {
      const startTime = Date.now();

      // 记录请求开始
      this.logger.debug(`${req.method} ${req.url}`);

      // 响应完成时记录日志
      res.on('finish', () => {
        const duration = Date.now() - startTime;
        const statusCode = res.statusCode;
        let logMethod;

        if (statusCode >= 500) {
          logMethod = this.logger.error.bind(this.logger);
        } else if (statusCode >= 400) {
          logMethod = this.logger.warn.bind(this.logger);
        } else {
          logMethod = this.logger.info.bind(this.logger);
        }

        logMethod(`${req.method} ${req.url} ${statusCode} ${duration}ms`);
      });

      next();
    };
  }

  /**
   * 配置基本认证
   */
  private configureBasicAuth(app: Express): void {
    const {username, password} = this.config.security.basicAuth;

    if (!username || !password) {
      this.logger.error('基本认证已启用，但用户名或密码未设置');
      return;
    }

    const users: Record<string, string> = {};
    users[username] = password;

    app.use(basicAuth({
      users,
      challenge: true,
      realm: 'NGate static server'
    }));

    this.logger.info('已启用基本认证');
  }

  /**
   * 配置代理中间件
   */
  private configureProxyMiddleware(app: Express): void {
    const proxyRules = this.config.proxyRules;

    if (!proxyRules || Object.keys(proxyRules).length === 0) {
      return;
    }

    for (const [path, target] of Object.entries(proxyRules)) {
      if (!path || !target) continue;

      try {
        app.use(
          path,
          createProxyMiddleware({
            target,
            changeOrigin: true,
            pathRewrite: {[`^${path}`]: ''},
            logLevel: 'silent', // 使用我们自己的日志
            onProxyReq: (proxyReq, req) => {
              this.logger.debug(`代理请求: ${req.method} ${req.url} -> ${target}`);
            }
          })
        );
        this.logger.info(`已配置代理: ${path} -> ${target}`);
      } catch (error) {
        this.logger.error(`配置代理失败: ${path} -> ${target}, 错误: ${error instanceof Error ? error.message : String(error)}`);
      }
    }
  }

  /**
   * 配置静态文件服务
   */
  private configureStaticServer(app: Express): void {
    const staticOptions = {
      maxAge: this.config.cacheMaxAge * 1000, // 转换为毫秒
      index: ['index.html', 'index.htm'],
      fallthrough: true,
      redirect: true
    };

    app.use(express.static(this.config.directory, staticOptions));

    // 目录列表
    app.use(this.createDirectoryListingMiddleware());

    // 404处理
    app.use((req: Request, res: Response) => {
      // 检查是否配置了自定义404页面
      if (this.config.errorPages?.notFound) {
        const notFoundPath = path.join(this.config.directory, this.config.errorPages?.notFound);

        if (fs.existsSync(notFoundPath)) {
          return res.status(404).sendFile(notFoundPath);
        }
      }

      // 默认404页面
      res.status(404).send(`
        <!DOCTYPE html>
        <html>
        <head>
          <meta charset="utf-8">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <title>404 - 找不到文件</title>
          <style>
            body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 800px; margin: 0 auto; padding: 20px; }
            h1 { color: #e74c3c; }
            pre { background: #f8f8f8; padding: 10px; border-radius: 4px; }
          </style>
        </head>
        <body>
          <h1>404 - 找不到文件</h1>
          <p>请求的路径: <code>${req.url}</code> 不存在。</p>
          <p><a href="/">返回首页</a></p>
        </body>
        </html>
      `);
    });

    // 错误处理中间件
    app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
      this.logger.error(`服务器错误: ${err.message}`);

      // 检查是否配置了自定义500页面
      if (this.config.errorPages?.serverError) {
        const errorPath = path.join(this.config.directory, this.config.errorPages?.serverError);

        if (fs.existsSync(errorPath)) {
          return res.status(500).sendFile(errorPath);
        }
      }

      // 默认500页面
      res.status(500).send(`
        <!DOCTYPE html>
        <html>
        <head>
          <meta charset="utf-8">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <title>500 - 服务器错误</title>
          <style>
            body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 800px; margin: 0 auto; padding: 20px; }
            h1 { color: #e74c3c; }
            pre { background: #f8f8f8; padding: 10px; border-radius: 4px; overflow: auto; }
          </style>
        </head>
        <body>
          <h1>500 - 服务器错误</h1>
          <p>处理请求时发生错误。</p>
          ${process.env.NODE_ENV === 'production' ? '' : '<pre>' + err.stack + '</pre>'}
          <p><a href="/">返回首页</a></p>
        </body>
        </html>
      `);
    });
  }

  /**
   * 创建目录列表中间件
   */
  private createDirectoryListingMiddleware(): RequestHandler {
    return (req, res, next) => {
      // 如果目录浏览被禁用，则跳过
      if (!this.config.directoryBrowsing.enabled) {
        return next();
      }

      // 如果不是GET或HEAD请求，或者已经有响应，则跳过
      if (!['GET', 'HEAD'].includes(req.method) || res.headersSent) {
        return next();
      }

      const reqPath = decodeURIComponent(req.path);
      const targetPath = path.join(this.config.directory, reqPath);

      // 检查路径是否存在，是否是目录，及是否尝试访问父目录（安全检查）
      try {
        const stat = require('fs').statSync(targetPath);
        if (!stat.isDirectory()) {
          return next();
        }
      } catch (err) {
        return next();
      }

      // 检查目录中是否有索引文件
      const indexFiles = ['index.html', 'index.htm'];
      for (const indexFile of indexFiles) {
        const indexPath = path.join(targetPath, indexFile);
        if (require('fs').existsSync(indexPath)) {
          return next(); // 存在索引文件，让静态中间件处理
        }
      }

      // 读取目录内容
      const fs = require('fs');
      fs.readdir(targetPath, (err: Error | null, files: string[]) => {
        if (err) {
          return next(err);
        }

        // 过滤隐藏文件
        let filteredFiles = files;
        if (!this.config.directoryBrowsing.showHiddenFiles) {
          filteredFiles = files.filter(file => !file.startsWith('.'));
        }

        // 获取目录项信息
        const fileDetails = filteredFiles.map(file => {
          const filePath = path.join(targetPath, file);
          try {
            const stats = fs.statSync(filePath);
            return {
              name: file,
              isDirectory: stats.isDirectory(),
              size: stats.size,
              mtime: stats.mtime.toISOString().split('T')[0]
            };
          } catch (err) {
            return {
              name: file,
              isDirectory: false,
              size: 0,
              mtime: 'Unknown'
            };
          }
        });

        // 排序文件
        fileDetails.sort((a, b) => {
          // 首先按照目录/文件分类
          if (a.isDirectory && !b.isDirectory) return -1;
          if (!a.isDirectory && b.isDirectory) return 1;

          // 然后按照指定字段排序
          const sortBy = this.config.directoryBrowsing.sortBy;
          const sortOrder = this.config.directoryBrowsing.sortOrder === 'asc' ? 1 : -1;

          if (sortBy === 'size') {
            return sortOrder * (a.size - b.size);
          } else if (sortBy === 'mtime') {
            return sortOrder * (a.mtime.localeCompare(b.mtime));
          } else {
            // 默认按名称排序
            return sortOrder * a.name.localeCompare(b.name);
          }
        });

        // 计算文件统计信息
        const totalFiles = fileDetails.filter(file => !file.isDirectory).length;
        const totalDirectories = fileDetails.filter(file => file.isDirectory).length;
        const totalSize = fileDetails.reduce((sum, file) => sum + file.size, 0);

        // 替换标题模板中的变量
        const title = this.config.directoryBrowsing?.title?.replace('{path}', reqPath);

        // 生成HTML
        const parentPath = reqPath === '/' ? null : path.dirname(reqPath) === '/' ? '/' : path.dirname(reqPath);
        const html = `
          <!DOCTYPE html>
          <html>
          <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <title>${title}</title>
            <style>
              body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 1000px; margin: 0 auto; padding: 20px; }
              h1 { border-bottom: 1px solid #eee; padding-bottom: 10px; margin-bottom: 15px; font-size: 18px; }
              table { width: 100%; border-collapse: collapse; margin-top: 20px; }
              th, td { text-align: left; padding: 8px; border-bottom: 1px solid #eee; }
              th { background-color: #f8f8f8; }
              a { color: #3498db; text-decoration: none; display: inline-flex; align-items: center; }
              a:hover { text-decoration: underline; }
              .directory { font-weight: bold; }
              .size { text-align: right; }
              .stats { margin-bottom: 20px; background-color: #f8f8f8; padding: 10px; border-radius: 4px; }
              .stats p { margin: 0; color: #555; text-align: center; }
              @media (max-width: 768px) {
                .stats p { text-align: left; }
              }
              ${this.config.directoryBrowsing?.customCSS}
            </style>
          </head>
          <body>
            <h1>${title}</h1>
            ${this.config.directoryBrowsing?.showStats ? `
            <div class="stats">
              <p>📊 ${totalFiles} 个文件 | 📁 ${totalDirectories} 个文件夹 | 📦 总大小: ${FileUtils.formatFileSize(totalSize)}</p>
            </div>
            ` : ''}
            <table>
              <thead>
                <tr>
                  <th>文件名</th>
                  <th>最后修改时间</th>
                  <th class="size">大小</th>
                </tr>
              </thead>
              <tbody>
                ${parentPath !== null ? `
                <tr>
                  <td><a href="${parentPath}">${this.config.directoryBrowsing?.showIcons ? '📂 ' : ''}../</a></td>
                  <td>-</td>
                  <td class="size">-</td>
                </tr>
                ` : ''}
                ${fileDetails.map(file => `
                <tr>
                  <td>
                    <a href="${path.join(reqPath, file.name)}${file.isDirectory ? '/' : ''}" 
                       class="${file.isDirectory ? 'directory' : ''}">
                      ${this.config.directoryBrowsing?.showIcons ? FileUtils.getFileIcon(file.name, file.isDirectory) : ''}${file.name}${file.isDirectory ? '/' : ''}
                    </a>
                  </td>
                  <td>${file.mtime}</td>
                  <td class="size">${file.isDirectory ? '-' : FileUtils.formatFileSize(file.size)}</td>
                </tr>
                `).join('')}
              </tbody>
            </table>
            ${this.config.directoryBrowsing?.showFooter ? `
            <p>${this.config.directoryBrowsing?.footerText}</p>
            ` : ''}
          </body>
          </html>
        `;

        res.setHeader('Content-Type', 'text/html; charset=utf-8');
        res.send(html);
      });
    };
  }
}
